﻿Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Imports System.Reflection

Namespace Load

    ''' <summary>
    ''' テーブルエイリアス
    ''' </summary>
    ''' <remarks></remarks>
    Public MustInherit Class TableAlias

#Region "Config"
        ''' <summary>
        ''' ルートテーブルエイリアス名の書式
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>
        ''' {0}　テーブルエイリアスのインデックス番号
        ''' </remarks>
        Public Shared Property ROOT_TABLE_ALIAS_FORMAT As String = "T{0}"

        ''' <summary>
        ''' ネストされたテーブルエイリアス名の書式
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>
        ''' {0}　リレーション元となるテーブルエイリアス名
        ''' {1}　テーブルエイリアスのインデックス番号
        ''' </remarks>
        Public Shared Property NEST_TABLE_ALIAS_FORMAT As String = "{0}_{1}"
#End Region

#Region "Basic info"
        ''' <summary>
        ''' ID
        ''' </summary>
        ''' <remarks>読み込み定義ごとに一意です</remarks>
        Public Property Index As Integer

        ''' <summary>
        ''' ルートかどうかを示します
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Overridable ReadOnly Property IsRoot As Boolean
            Get
                Return True
            End Get
        End Property

        ''' <summary>
        ''' カスケード階層
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>ルートテーブルの場合は0です。</remarks>
        Public Property CascadeLevel As Integer = 0

        Private _talbe As TableAttribute
        ''' <summary>
        ''' テーブル属性
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Table As TableAttribute
            Get
                Return _talbe
            End Get
            Set(value As TableAttribute)
                _talbe = value
                'Me.BuildColumnAliases()
            End Set
        End Property

        Private _loadTableFullName As String
        ''' <summary>
        ''' 読み込みテーブル名正式名
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LoadTableFullName As String
            Get
                Return If(String.IsNullOrEmpty(_loadTableFullName), Me.Table.LoadTableFullName, _loadTableFullName)
            End Get
            Set(value As String)
                _loadTableFullName = value
            End Set
        End Property

        ''' <summary>
        ''' 列別名リスト
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property ColumnAliases As New List(Of ColumnAlias)

        ''' <summary>
        ''' 関連元テーブルエイリアス
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>ルートには存在しませんが、関連読み込み定義には存在します。</remarks>
        Public Property BaseTableAlias As TableAlias

        Public Property ActualTableAlias As TableAlias

        ''' <summary>
        ''' テーブルエイリアス名
        ''' </summary>
        ''' <remarks></remarks>
        Friend TableAliasName As String

        ''' <summary>
        ''' テーブル別名（完全）
        ''' </summary>
        ''' <remarks>SQLで使用します</remarks>
        Public ReadOnly Property TableAliasFullName() As String
            Get
                If Me.BaseTableAlias Is Nothing Then
                    Return Me.TableAliasName
                Else
                    Return String.Format(TableAlias.NEST_TABLE_ALIAS_FORMAT, Me.BaseTableAlias.TableAliasFullName, Me.TableAliasName)
                End If
            End Get
        End Property

        ''' <summary>
        ''' Nullを許容するかを示します
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        MustOverride ReadOnly Property IsNullable As Boolean

#Region "Relation table Aliases"
        Dim _relationTableAliases As ObservableCollection(Of RelationTableAlias)
        ''' <summary>
        ''' 関連テーブルエイリアスリスト
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>上層方向のテーブルエイリアス</remarks>
        Public ReadOnly Property RelationTableAliases As ObservableCollection(Of RelationTableAlias)
            Get
                If _relationTableAliases Is Nothing Then
                    _relationTableAliases = New ObservableCollection(Of RelationTableAlias)
                    AddHandler _relationTableAliases.CollectionChanged, AddressOf OnRelationsTablesChanged
                End If
                Return _relationTableAliases
            End Get
        End Property

        ''' <summary>
        ''' 関連テーブルエイリアスアイテム更新時
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        ''' <remarks></remarks>
        Private Sub OnRelationsTablesChanged(ByVal sender As Object, ByVal e As NotifyCollectionChangedEventArgs)
            If e.NewItems IsNot Nothing AndAlso e.NewItems.Count <> 0 Then
                '追加した場合
                For Each item As RelationTableAlias In e.NewItems
                    item.BaseTableAlias = Me
                    If Me.IsNullable Then
                        item.SetIsNullable(True) '外部結合を継承する
                    Else
                        item.SetIsNullable(item.Relations.First.IsNullable)
                    End If
                Next
            End If
        End Sub
#End Region

#End Region

#Region "Build SQL"
        ''' <summary>
        ''' SELECT句を取得します
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public MustOverride Function GetSelectText() As String

        ''' <summary>
        ''' FROM句を取得します
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public MustOverride Function GetFromText() As String

        ''' <summary>
        ''' カスケード情報を再生成します
        ''' </summary>
        ''' <param name="creater">親テーブルエイリアス生成関数</param>
        ''' <param name="register">テーブルエイリアスID生成関数</param>
        ''' <remarks></remarks>
        Public Overridable Sub Build(creater As Func(Of TableAlias, PropertyInfo, Integer, RelationTableAlias), register As Func(Of TableAlias, Integer))
            '自身の登録
            Me.Index = register.Invoke(Me)

            'テーブル名生成
            Me.BuildTableAliasName()

            '列エイリアス作成
            Me.ColumnAliases = ColumnAliasFactory.Create(Me)

            '基底カスケード処理
            If Me.Table.SuperRelations.Any Then
                '基底カスケードの場合、カスケードレベルをインクリメントしません。
                Dim def As RelationTableAlias = creater.Invoke(Me, Nothing, Me.CascadeLevel)
                Me.RelationTableAliases.Add(def)
                '再帰
                def.Build(creater, register)
            End If

            '上層カスケード処理
            For Each item In (From el In Me.Table.Columns Where el.IsParent Select el.PropertyInfo Distinct)
                '上層カスケードの場合、カスケードレベルをインクリメントします。
                Dim def As RelationTableAlias = creater.Invoke(Me, item, Me.CascadeLevel + 1)
                If def Is Nothing Then Continue For
                Me.RelationTableAliases.Add(def)
                '再帰
                def.Build(creater, register)
            Next
        End Sub

        ''' <summary>
        ''' テーブルエイリアス名を構築
        ''' </summary>
        ''' <remarks></remarks>
        Private Sub BuildTableAliasName()
            Me.TableAliasName = String.Format(TableAlias.ROOT_TABLE_ALIAS_FORMAT, Me.Index)
        End Sub
#End Region

        ''' <summary>
        ''' すべての列エイリアスを取得します
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected Function GetAllColumnAliases() As List(Of ColumnAlias)
            Dim lst As New List(Of ColumnAlias)

            '自身の列を追加
            lst.AddRange(Me.ColumnAliases)

            'リレーションへカスケード
            For Each item In Me.RelationTableAliases
                lst.AddRange(item.GetAllColumnAliases)
            Next

            Return lst
        End Function

    End Class

End Namespace
